﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Content;
using System.Xml.Serialization;
using System.IO;

namespace FrogEngine
{
	/// <summary>
	/// File Format: Adjacency Matrix (kind of)... string at beginning of each line is a possible state, and 
	/// all white space separated strings that follow are the names of possible transition states
	///		[StateName1] [Transition1] [Transition2] ...
	/// </summary>
	public abstract class FSM : IFSM 
	{
		/// <summary>
		/// Use this constructor if you intend to construct the FSM by hand in code.
		/// </summary>
		public FSM()
		{
			states = new HashSet<IFSMState>();
		}

		/// <summary>
		/// Use this constructor to construct an FSM based on an Adjacency Matrix defined 
		/// in a file
		/// </summary>
		/// <param name="fileName">Name of the file containing the adjacency matrix</param>
		public FSM(string fileName)
		{
			states = new HashSet<IFSMState>();
			LoadFSMFromFile(fileName);
		}

		protected IFSMState currentState;
		public IFSMState CurrentState
		{
			get { return currentState; }
		}

		protected HashSet<IFSMState> states;
		public ISet<IFSMState> States
		{
			get { return states; }
		}

		public void AddState(IFSMState state)
		{
			states.Add(state);
		}

		public void RemoveState(IFSMState state)
		{
			if (currentState == state)
				currentState = null;
			states.Remove(state);
		}

		public virtual void Transition(IFSMState nextState)
		{
			if (currentState == null)
				currentState = nextState;
			else if (currentState.IsValidTransition(nextState))
			{
				currentState.OnLeaving();
				currentState = nextState;
				currentState.OnEntering();
			}
			else throw new FSMInvalidTransitionException(currentState, nextState);
		}

		public virtual void Update(GameTime gameTime)
		{
			if (currentState != null)
				currentState.Update(gameTime);
		}

		/// <summary>
		/// Override this method in order to specify a mapping from 
		/// the state names in the file (which are just strings) to 
		/// your strongly typed FSMStates.
		/// </summary>
		/// <param name="fileName"></param>
		public abstract void LoadFSMFromFile(string fileName);

		protected Dictionary<string, List<string>> ReadFile(string fileName)
		{
			Dictionary<string, List<string>> adjMatrix = new Dictionary<string, List<string>>();

			using (FileStream stream = new FileStream(fileName, FileMode.Open))
			{
				using (StreamReader reader = new StreamReader(stream))
				{
					while (!reader.EndOfStream)
					{
						string[] nodes = reader.ReadLine().Split(' ');
						int count = nodes.Count();
						List<string> trans = new List<string>();

						for (int i = 1; i < count; i++)
							trans.Add(nodes[i]);

						adjMatrix.Add(nodes[0], trans);
					}
				}
			}
			
			return adjMatrix;
		}
	}
}
